#include <stdio.h>

#include "casl.h"

#include "os.h"

#include "instructions.h"

/* memory.c */
int memoryWrite (UWORD address, WORD value);
WORD memoryRead (UWORD address);

REGISTER reg, initreg;

/* Machine(Comet) & Macro Operation Table */
MOT mot[] = {
  {"", ZERO, 0, BLANK},		/* ZERO */
  {"LD", LD, 2, GR_E}, {"ST", ST, 2, GR_E}, {"LEA", LEA, 2, GR_E},
  {"ADD", ADD, 2, GR_E}, {"SUB", SUB, 2, GR_E}, {"AND", AND, 2, GR_E}, {"OR",
									OR, 2,
									GR_E},
    {"EOR", EOR, 2, GR_E},
  {"CPA", CPA, 2, GR_E}, {"CPL", CPL, 2, GR_E},
  {"SLA", SLA, 2, GR_E}, {"SRA", SRA, 2, GR_E}, {"SLL", SLL, 2, GR_E}, {"SRL",
									SRL,
									2,
									GR_E},
  {"JMP", JMP, 2, E}, {"JPZ", JPZ, 2, E}, {"JMI", JMI, 2, E}, {"JNZ", JNZ, 2,
							       E}, {"JZE",
								    JZE, 2,
								    E},
  {"PUSH", PUSH, 2, E}, {"POP", POP, 2, GR}, {"CALL", CALL, 2, E}, {"RET",
								    RET, 2,
								    BLANK},
  {"IN", IN, 6, ALABEL_NLABEL}, {"OUT", OUT, 6, ALABEL_NLABEL}, {"EXIT", EXIT, 2, BLANK}	/* Macro Operation */
};

/* Stack Operation */
static void
push (WORD number)
{
  memoryWrite (--reg.GR[4], number);
}

static WORD
pop (void)
{
  WORD number = 0;

  if ((reg.GR[4] - initreg.GR[4]) % MEMORY_SIZE == 0)
    display (_("Stack is empty.\n"));
  else
    number = memoryRead (reg.GR[4]++);

  return number;
}

#define Eaddr(ADR,XRno) (((ADR)+((XRno)==0?0:reg.GR[XRno]))%MEMORY_SIZE)

static void
Flags (WORD number)		/* Set Flags Register: FR */
{
  if (number == 0)
    reg.FR = 1;
  else if (number > 0)
    reg.FR = 0;
  else
    reg.FR = 2;
}

/* COMET */
void
LD (WORD GRno, WORD ADR, WORD XRno)
{
  reg.GR[GRno] = memoryRead (Eaddr (ADR, XRno));
}

void
ST (WORD GRno, WORD ADR, WORD XRno)
{
  memoryWrite (Eaddr (ADR, XRno), reg.GR[GRno]);
}

void
LEA (WORD GRno, WORD ADR, WORD XRno)
{
  reg.GR[GRno] = Eaddr (ADR, XRno);
  Flags (reg.GR[GRno]);
}

void
ADD (WORD GRno, WORD ADR, WORD XRno)
{
  reg.GR[GRno] += memoryRead (Eaddr (ADR, XRno));
  Flags (reg.GR[GRno]);
}

void
SUB (WORD GRno, WORD ADR, WORD XRno)
{
  reg.GR[GRno] -= memoryRead (Eaddr (ADR, XRno));
  Flags (reg.GR[GRno]);
}

void
AND (WORD GRno, WORD ADR, WORD XRno)
{
  reg.GR[GRno] &= memoryRead (Eaddr (ADR, XRno));
  Flags (reg.GR[GRno]);
}

void
OR (WORD GRno, WORD ADR, WORD XRno)
{
  reg.GR[GRno] |= memoryRead (Eaddr (ADR, XRno));
  Flags (reg.GR[GRno]);
}

void
EOR (WORD GRno, WORD ADR, WORD XRno)
{
  reg.GR[GRno] ^= memoryRead (Eaddr (ADR, XRno));
  Flags (reg.GR[GRno]);
}

void
CPA (WORD GRno, WORD ADR, WORD XRno)
{
  Flags (reg.GR[GRno] - memoryRead (Eaddr (ADR, XRno)));
}

void
CPL (WORD GRno, WORD ADR, WORD XRno)
{
  Flags ((UWORD) GRno - (UWORD) memoryRead (Eaddr (ADR, XRno)));
}

void
SLA (WORD GRno, WORD ADR, WORD XRno)
{
  reg.GR[GRno] = reg.GR[GRno] << Eaddr (ADR, XRno);
  Flags (reg.GR[GRno]);
}

void
SRA (WORD GRno, WORD ADR, WORD XRno)
{
  reg.GR[GRno] = reg.GR[GRno] >> Eaddr (ADR, XRno);
  Flags (reg.GR[GRno]);
}

void
SLL (WORD GRno, WORD ADR, WORD XRno)
{
  reg.GR[GRno] = (UWORD) reg.GR[GRno] << Eaddr (ADR, XRno);
  Flags (reg.GR[GRno]);
}

void
SRL (WORD GRno, WORD ADR, WORD XRno)
{
  reg.GR[GRno] = (UWORD) reg.GR[GRno] >> Eaddr (ADR, XRno);
  Flags (reg.GR[GRno]);
}

void
JMP (WORD GRno, WORD ADR, WORD XRno)
{
  reg.PC = Eaddr (ADR, XRno) - 2;
}

void
JPZ (WORD GRno, WORD ADR, WORD XRno)
{
  if (reg.FR == 0 || reg.FR == 1)
    reg.PC = Eaddr (ADR, XRno) - 2;
}

void
JMI (WORD GRno, WORD ADR, WORD XRno)
{
  if (reg.FR == 2)
    reg.PC = Eaddr (ADR, XRno) - 2;
}

void
JNZ (WORD GRno, WORD ADR, WORD XRno)
{
  if (reg.FR == 0 || reg.FR == 2)
    reg.PC = Eaddr (ADR, XRno) - 2;
}

void
JZE (WORD GRno, WORD ADR, WORD XRno)
{
  if (reg.FR == 1)
    reg.PC = Eaddr (ADR, XRno) - 2;
}

void
PUSH (WORD GRno, WORD ADR, WORD XRno)
{
  push (Eaddr (ADR, XRno));
}

void
POP (WORD GRno, WORD ADR, WORD XRno)
{
  reg.GR[GRno] = pop ();
}

void
CALL (WORD GRno, WORD ADR, WORD XRno)
{
  push (reg.PC + 2);
  reg.PC = Eaddr (ADR, XRno) - 2;
}

void
RET (WORD GRno, WORD ADR, WORD XRno)
{
  if ((reg.GR[4] - initreg.GR[4]) % MEMORY_SIZE == 0)
    reg.PC = end_PC - 2;	/* if it is a sub program, exit */
  else
    reg.PC = pop () - 2;
}

void
IN (WORD x, WORD y, WORD z)	/* macro instruction IN */
{
  WORD alabel, nlabel;

  nlabel = pop ();

  alabel = pop ();

  input (alabel, nlabel);	/* input a string, seeing io.c */
}

void
OUT (WORD x, WORD y, WORD z)	/* macro instruction OUT */
{
  WORD alabel, nlabel;

  nlabel = pop ();

  alabel = pop ();

  output (alabel, nlabel);	/* output a string, seeing io.c */
}

void
EXIT (WORD x, WORD y, WORD z)	/* macro instruction EXIT */
{
  reg.PC = end_PC - 2;
}

void
ZERO (WORD GRno, WORD ADR, WORD XRno)
{
  reg.PC = end_PC - 2;
  display (_("Warning: this is not program.\n"));
}
